package cn.turboinfo.fuyang.api.domain.mini.usecase.contract;

import cn.turboinfo.fuyang.api.domain.common.service.company.HousekeepingCompanyService;
import cn.turboinfo.fuyang.api.domain.common.service.contract.ContractService;
import cn.turboinfo.fuyang.api.domain.common.service.division.DivisionService;
import cn.turboinfo.fuyang.api.domain.common.service.file.FileAttachmentService;
import cn.turboinfo.fuyang.api.domain.common.service.order.PayOrderService;
import cn.turboinfo.fuyang.api.domain.common.service.order.ServiceOrderService;
import cn.turboinfo.fuyang.api.domain.common.service.product.ProductService;
import cn.turboinfo.fuyang.api.domain.common.usecase.AbstractUseCase;
import cn.turboinfo.fuyang.api.domain.mini.helper.ContractHelper;
import cn.turboinfo.fuyang.api.domain.util.ContractUtils;
import cn.turboinfo.fuyang.api.domain.web.component.file.FileAttachmentHelper;
import cn.turboinfo.fuyang.api.domain.web.component.file.FileRefTypeConstant;
import cn.turboinfo.fuyang.api.entity.common.enumeration.common.EntityObjectType;
import cn.turboinfo.fuyang.api.entity.common.enumeration.contract.ContractStatus;
import cn.turboinfo.fuyang.api.entity.common.enumeration.order.PayOrderStatus;
import cn.turboinfo.fuyang.api.entity.common.pojo.category.CategoryUpdater;
import cn.turboinfo.fuyang.api.entity.common.pojo.company.HousekeepingCompany;
import cn.turboinfo.fuyang.api.entity.common.pojo.contract.Contract;
import cn.turboinfo.fuyang.api.entity.common.pojo.contract.ContractCreator;
import cn.turboinfo.fuyang.api.entity.common.pojo.contract.ContractTmpl;
import cn.turboinfo.fuyang.api.entity.common.pojo.contract.ContractUpdater;
import cn.turboinfo.fuyang.api.entity.common.pojo.division.Division;
import cn.turboinfo.fuyang.api.entity.common.pojo.file.FileAttachmentCreator;
import cn.turboinfo.fuyang.api.entity.common.pojo.order.ServiceOrder;
import cn.turboinfo.fuyang.api.entity.common.pojo.product.Product;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanUpdaterHelper;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import java.io.*;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Component
public class MiniContractCreateUseCase extends AbstractUseCase<MiniContractCreateUseCase.InputData, MiniContractCreateUseCase.OutputData> {
    private final ContractService contractService;

    private final FileAttachmentService fileAttachmentService;

    private final ServiceOrderService serviceOrderService;

    private final ProductService productService;

    private final HousekeepingCompanyService housekeepingCompanyService;

    private final DivisionService divisionService;

    private final PayOrderService payOrderService;

    private final FileAttachmentHelper fileAttachmentHelper;

    private final ContractHelper contractHelper;

    private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy 年 MM 月 dd 日");
    private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");

    @Override
    protected OutputData doAction(InputData inputData) {

        Long signatureFileId = inputData.getSignatureFileId();

        ContractCreator.Builder builder = ContractCreator.builder();

        QBeanUpdaterHelper.copyPropertiesToUpdateBuilder(builder, ContractUpdater.class, inputData);

        ServiceOrder serviceOrder = serviceOrderService.getByIdEnsure(inputData.getOrderId());

        if (!serviceOrder.getUserId().equals(inputData.getUserId())) {
            throw new RuntimeException("不能查看他人的合同");
        }

        ContractTmpl contractTmpl = contractHelper.getContractTmpl(serviceOrder);

        // 没找到模版抛出异常
        if (contractTmpl == null) {
            throw new RuntimeException("没有合同模版");
        }

        Optional<Contract> optionalContract = contractService.findByOrderId(serviceOrder.getId());

        // 删除旧合同
        optionalContract.ifPresent(contract -> contractService.deleteById(contract.getId()));

        // 保存合同
        Contract contract = contractService.save(builder
                .withContractNo(StringUtils.EMPTY)
                .withCompanyId(serviceOrder.getCompanyId())
                .withTemplateId(contractTmpl.getId())
                .withStatus(ContractStatus.INIT)
                .build());


        Product product = productService.getByIdEnsure(serviceOrder.getProductId());

        HousekeepingCompany company = housekeepingCompanyService.getByIdEnsure(serviceOrder.getCompanyId());


        LocalDate now = LocalDate.now();
        String dateStr = now.format(dateFormatter);
        String contractNo = ContractUtils.generateContractCode(serviceOrder.getCompanyId(), serviceOrder.getCategoryId(), contract.getId());
        // 替换内容
        Map<String, Object> mappingValueMap = new HashMap<>();
        mappingValueMap.put("contractId", contractNo);
        mappingValueMap.put("contractDate", dateStr);
        mappingValueMap.put("companyName", company.getName());
        mappingValueMap.put("legalPerson", company.getLegalPerson());
        mappingValueMap.put("uscc", company.getUscc());
        mappingValueMap.put("serviceContent", product.getName());
        mappingValueMap.put("companyMobile", company.getContactNumber());
        mappingValueMap.put("companyAddress", company.getRegisteredAddress());
        mappingValueMap.put("consumer", serviceOrder.getContact());
        mappingValueMap.put("consumerMobile", serviceOrder.getMobile());
        mappingValueMap.put("serviceMode", "________________");
        mappingValueMap.put("serviceDate", String.format("%s 到 %s", serviceOrder.getScheduledStartTime().format(dateFormatter), serviceOrder.getScheduledEndTime().format(dateFormatter)));
        mappingValueMap.put("serviceTime", String.format("%s 到 %s", serviceOrder.getScheduledStartTime().format(timeFormatter), serviceOrder.getScheduledEndTime().format(timeFormatter)));
        mappingValueMap.put("serviceCharge", serviceOrder.getPrice());
        mappingValueMap.put("settlementMethod", "________________");
        mappingValueMap.put("paymentMethod", StringUtils.EMPTY);
        mappingValueMap.put("paymentDate", "  年  月  日");
        mappingValueMap.put("consumerSignature", "________________");
        mappingValueMap.put("signatureDate", "  年  月  日");
        mappingValueMap.put("serviceAddress", "________________________________");

        // 查询地区
        Division division = divisionService.getById(serviceOrder.getDivisionId()).orElse(null);
        if (division != null) {
            Map<Long, Division> divisionMap = divisionService.findByIdCollection(Arrays.asList(Long.parseLong(StringUtils.rightPad(division.getProvinceCode().toString(), 6, "0")), Long.parseLong(StringUtils.rightPad(division.getCityCode().toString(), 6, "0"))))
                    .stream()
                    .collect(Collectors.toMap(Division::getAreaCode, o -> o));
            String address = StringUtils.EMPTY;
            if (divisionMap.containsKey(division.getProvinceCode())) {
                address += divisionMap.get(division.getProvinceCode()).getAreaName() + " 省";
            }
            if (divisionMap.containsKey(division.getCityCode())) {
                address += " " + divisionMap.get(division.getCityCode()).getAreaName() + " 市";
            }
            address += " " + division.getAreaName() + " " + serviceOrder.getPoiName() + " " + serviceOrder.getDetail();
            mappingValueMap.put("serviceAddress", address);
        }

        // 查询支付
        payOrderService.findByObjectIdAndStatus(EntityObjectType.SERVICE_ORDER, serviceOrder.getId(), PayOrderStatus.PAID)
                .stream()
                .findFirst()
                .ifPresent(payOrder -> {
                    mappingValueMap.put("paymentMethod", payOrder.getPayType().getName());
                    mappingValueMap.put("paymentDate", payOrder.getPaidTime().format(dateFormatter));
                });

        // 签名文件
        File signatureFile = fileAttachmentHelper.readFileAttachment(signatureFileId);
        try (InputStream is = new FileInputStream(signatureFile)) {
            mappingValueMap.put("consumerSignature", IOUtils.toByteArray(is));
            mappingValueMap.put("signatureDate", IOUtils.toByteArray(is));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        String displayName = String.format("%s_%s_%s.pdf",
                product.getName(),
                contractTmpl.getName(),
                serviceOrder.getContact()
        );

        String relativePath = String.format("order/contract/%s/%s", now.getYear(), now.getMonthValue());
        String fileExt = FilenameUtils.getExtension(displayName).toLowerCase();
        String filename = UUID.randomUUID() + "." + fileExt;
        Path absolutePath = fileAttachmentService.resolveAbsolutePath(relativePath);

        // 生成合同
        try (FileOutputStream os = new FileOutputStream(absolutePath.resolve(filename).toFile())) {
            contractHelper.generateContractPDF(contractTmpl, mappingValueMap, os);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 更新合同状态
        ContractUpdater.Builder updater = ContractUpdater.builder(contract.getId());
        QBeanUpdaterHelper.copyPropertiesToUpdateBuilder(updater, CategoryUpdater.class, contract);

        contractService.update(updater
                .withContractNo(contractNo)
                .withStatus(ContractStatus.SIGNED)
                .build());

        {
            // 保存合同文件
            FileAttachmentCreator creator = FileAttachmentCreator.builder()
                    .withDisplayName(displayName)
                    .withFilename(filename)
                    .withOriginalFilename(displayName)
                    .withRefType(FileRefTypeConstant.CONTRACT_FILE)
                    .withRefId(contract.getId())
                    .withRelativePath(relativePath)
                    .build();
            fileAttachmentService.save(creator);
        }
        {
            // 更新用户签名文件关联
            fileAttachmentService.updateRefId(signatureFileId, contract.getId());
        }
        return OutputData.builder()
                .contract(contract)
                .build();
    }

    @EqualsAndHashCode(
            callSuper = true
    )
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class InputData extends AbstractUseCase.InputData {

        @NotNull(
                message = "订单编码不能为空"
        )
        private Long orderId;

        @NotNull(
                message = "用户编码不能为空"
        )
        private Long userId;

        @NotNull(
                message = "签名文件不能为空"
        )
        private Long signatureFileId;

    }

    @Getter
    @Setter
    @Builder
    public static class OutputData extends AbstractUseCase.OutputData {
        private Contract contract;
    }
}
